home *** CD-ROM | disk | FTP | other *** search
/ NOVA - For the NeXT Workstation / NOVA - For the NeXT Workstation.iso / SourceCode / AdobeExamples / NX_ImportAdv / DrawingView.m < prev    next >
Text File  |  1992-12-19  |  51KB  |  2,129 lines

  1.  
  2. /*
  3.  * (a)  (C) 1990 by Adobe Systems Incorporated. All rights reserved.
  4.  *
  5.  * (b)  If this Sample Code is distributed as part of the Display PostScript
  6.  *    System Software Development Kit from Adobe Systems Incorporated,
  7.  *    then this copy is designated as Development Software and its use is
  8.  *    subject to the terms of the License Agreement attached to such Kit.
  9.  *
  10.  * (c)  If this Sample Code is distributed independently, then the following
  11.  *    terms apply:
  12.  *
  13.  * (d)  This file may be freely copied and redistributed as long as:
  14.  *    1) Parts (a), (d), (e) and (f) continue to be included in the file,
  15.  *    2) If the file has been modified in any way, a notice of such
  16.  *      modification is conspicuously indicated.
  17.  *
  18.  * (e)  PostScript, Display PostScript, and Adobe are registered trademarks of
  19.  *    Adobe Systems Incorporated.
  20.  * 
  21.  * (f) THE INFORMATION BELOW IS FURNISHED AS IS, IS SUBJECT TO
  22.  *    CHANGE WITHOUT NOTICE, AND SHOULD NOT BE CONSTRUED
  23.  *    AS A COMMITMENT BY ADOBE SYSTEMS INCORPORATED.
  24.  *    ADOBE SYSTEMS INCORPORATED ASSUMES NO RESPONSIBILITY
  25.  *    OR LIABILITY FOR ANY ERRORS OR INACCURACIES, MAKES NO
  26.  *    WARRANTY OF ANY KIND (EXPRESS, IMPLIED OR STATUTORY)
  27.  *    WITH RESPECT TO THIS INFORMATION, AND EXPRESSLY
  28.  *    DISCLAIMS ANY AND ALL WARRANTIES OF MERCHANTABILITY, 
  29.  *    FITNESS FOR PARTICULAR PURPOSES AND NONINFRINGEMENT
  30.  *    OF THIRD PARTY RIGHTS.
  31.  */
  32.  
  33. /*
  34.  *    DrawingView.m
  35.  *
  36.  *    This view represents the page that the image is drawn onto. It is
  37.  *    a subview of the DocView. The DocView is the document view of
  38.  *    the ClipView. This view's real size grows with the scale but the
  39.  *    bounds always stays the same.
  40.  *
  41.  *    An offscreen buffer, bufferId, is used to draw into and then
  42.  *    this buffer is composited onscreen. This technique allows for the
  43.  *    separation of the static drawing from the temporal drawing (modal
  44.  *    loop redrawing and control point display).  The static drawing takes
  45.  *    place in the buffer while the temporal drawing takes place in this view.
  46.  *    The static drawing is stuff that is complex and that will stay around for
  47.  *    a while. Saving this drawing in a buffer elimates having to redraw it
  48.  *    for something simple like drawing a control point or something.
  49.  *    (The bufferId is the content view of a plain window the size of the
  50.  *    content view of this view's window.)
  51.  *
  52.  *    Version:    2.0
  53.  *    Author:    Ken Fromm
  54.  *    History:
  55.  *            03-17-91        Added this comment and fixed the preview section.
  56.  */
  57.  
  58. #import "DrawingView.h"
  59. #import "DocView.h"
  60. #import "Document.h"
  61. #import "GraphicImport.h"
  62. #import "GraphicList.h"
  63. #import "ImportPanel.h"
  64. #import "SaveAsPanel.h"
  65. #import "epsf.h"
  66.  
  67. #import "DrawingViewWraps.h"
  68.  
  69. #import <appkit/Cell.h>
  70. #import <appkit/ClipView.h>
  71. #import <appkit/NXCursor.h>
  72. #import <appkit/NXImage.h>
  73. #import <appkit/NXBitmapImageRep.h>
  74. #import <appkit/Text.h>
  75. #import <appkit/Pasteboard.h>
  76. #import <appkit/PrintInfo.h>
  77. #import <appkit/nextstd.h>
  78.  
  79. #import <dpsclient/dpsclient.h>
  80. #import <dpsclient/wraps.h>
  81. #import <math.h>
  82.  
  83. extern char    ControlFont[ ];
  84.  
  85. extern const NXRect  DefaultContentRect;
  86.  
  87. static char    EpsfPboard[] = "Epsf Pasteboard Type";
  88.  
  89. static char    EpsfProcSet[] = "EPSF_Illustrator_abbrev 0 0";
  90.  
  91. /*
  92. *    Timers used to automatically scroll when the mouse is
  93. *    outside the drawing view and not moving.
  94. */
  95. static void startTimer(NXTrackingTimer ** timer, int *timermask, id window)
  96. {
  97.     if (!*timer)
  98.     {
  99.         *timer = NXBeginTimer(NULL, 0.15, 0.2);
  100.         *timermask = NX_TIMERMASK;
  101.         [window addToEventMask:NX_TIMERMASK];
  102.     }
  103. }
  104.  
  105. static void stopTimer(NXTrackingTimer **timer, int *timermask, id window)
  106. {
  107.     if (*timer)
  108.     {
  109.         NXEndTimer(*timer);
  110.         *timer = NULL;
  111.         *timermask = 0;
  112.         [window removeFromEventMask:NX_TIMERMASK];            
  113.     }
  114. }
  115.  
  116. void compositeBuffer(int gstate, const NXRect *srce, const NXPoint *dest, int op)
  117. {
  118.       PScomposite(NX_X(srce), NX_Y(srce), NX_WIDTH(srce), NX_HEIGHT(srce),
  119.                 gstate, dest->x, dest->y, op);
  120. }
  121.  
  122. /*
  123. *    The next two procedures are for the drawing buffer.
  124. *    This buffer is used to prevent unnecessary drawing by
  125. *    retaining images within offscreen windows.
  126. *
  127. *    Create a plain window the size of the rectangle passed in and
  128. *    then insert a view into the window as a subview. A clip view
  129. *    is swapped for the content view so the buffer can be scrolled
  130. *    when this view is scrolled.
  131. */
  132. static id createBuffer(const NXSize *winSize)
  133. {
  134.     id        buffer, clipview, window;
  135.  
  136.     NXRect    contRect;
  137.     
  138.     contRect.origin.x = contRect.origin.y = 0;
  139.     contRect.size = *winSize;
  140.     window = [Window newContent:&contRect
  141.                 style:NX_PLAINSTYLE
  142.                 backing:NX_RETAINED
  143.                 buttonMask:0
  144.                 defer:NO] ;
  145.  
  146.     buffer = [[[[View  newFrame:&contRect] allocateGState] setFlipped:NO] setClipping:NO];
  147.     clipview = [[[ClipView  new]  setFlipped:NO]  setDisplayOnScroll:NO];
  148.     [[window  setContentView:clipview ] free];
  149.     [clipview  setDocView:buffer];
  150.     [clipview  allocateGState];
  151.  
  152.     [window  display];
  153.  
  154.     return buffer;
  155. }
  156.  
  157. /*
  158. *    Resize the buffer and its window. Called when this view's
  159. *    window resizes. (The buffer is enlarged by a bit as an 
  160. *    added insurance.)
  161. */
  162. void resizeBuffer(id buffer, const NXSize*newSize)
  163. {
  164.     NXRect        frameRect, contRect;
  165.     
  166.     [buffer  getFrame:&frameRect];
  167.     if (newSize->width > frameRect.size.width || newSize->height > frameRect.size.height)
  168.     {
  169.         contRect.origin.x = contRect.origin.y = 0.0;
  170.         contRect.size = *newSize;
  171.         NXInsetRect(&contRect, -10.0, -10.0);
  172.         [Window  getFrameRect:&frameRect
  173.                     forContentRect:&contRect
  174.                         style:NX_PLAINSTYLE];
  175.         [[buffer window]  sizeWindow:frameRect.size.width :frameRect.size.height];
  176.         [buffer sizeTo:newSize->width :newSize->height];
  177.     }
  178. }
  179.  
  180. @implementation DrawingView
  181.  
  182. +newFrame:(const NXRect *) frameRect
  183. {    
  184.     self = [super newFrame:frameRect]; 
  185.     [[self allocateGState] setClipping:NO]; 
  186.     
  187.     graphiclistId = [GraphicList  new];
  188.     selectedlistId = [GraphicList  new];
  189.  
  190.     hitPoint = [NXApp  hitPoint];
  191.     upathBuffer = [NXApp upathBuffer];
  192.  
  193.     bufferId = createBuffer(&DefaultContentRect.size);
  194.  
  195.     return self;
  196. }
  197.  
  198. /* Free any unplaced imported object. */
  199. - free
  200. {
  201.     [graphicId  free];
  202.     [[graphiclistId  freeObjects]  free];
  203.     [selectedlistId  free];
  204.  
  205.     [[bufferId  window]  free];
  206.     
  207.     return [super  free];
  208. }
  209.  
  210. /*
  211. *    Aids for understanding the operation of the application.
  212. *    Moves the offscreen buffer onscreen.
  213. */
  214. - showBuffer:sender
  215. {
  216.     [[bufferId window] moveTo:10 :10];
  217.     [[bufferId window]  orderFront:self];
  218.  
  219.     [window  orderFront:self];
  220.  
  221.     return self;
  222. }
  223.  
  224. - hideBuffer:sender
  225. {
  226.     [[bufferId window]  orderOut:self];
  227.  
  228.     return self;
  229. }
  230.  
  231. /*
  232. *    Returns the size of the control point scaled to reflect the current scale.
  233. */
  234. - (float) controlPointSize
  235. {
  236.     return  FONTSIZE * (1.0/[superview  scale]);
  237. }
  238.  
  239. /*
  240. *    Scale the hit setting. Using an unscaled hit setting would be like
  241. *    using a boxing glove on a 400% scale.
  242. */
  243. - (float) hitSetting
  244. {    
  245.     return ([NXApp hitSetting] * (1.0/[superview  scale]));
  246. }
  247.  
  248. - buffer
  249. {
  250.     return bufferId;
  251. }
  252.  
  253. - image
  254. {
  255.     return imageId;
  256. }
  257.  
  258. - setDirty:(BOOL) flag
  259. {
  260.     dirty = flag;
  261.     [window  setDocEdited:flag];
  262.  
  263.     return self;
  264. }
  265.  
  266. - (BOOL)isDirty
  267. {
  268.     return dirty;
  269. }
  270.  
  271. - (BOOL)isEmpty
  272. {
  273.     return [graphiclistId count] == 0;
  274. }
  275.  
  276. - (BOOL)isSelected
  277. {
  278.     return [selectedlistId count] > 0;
  279. }
  280.  
  281. /*
  282. *    Set the cursor to be the intersection of the bounds
  283. *    and the visible portion of this view.
  284. */
  285. - resetCursorRects
  286. {
  287.     NXRect        visRect;
  288.  
  289.     [self getVisibleRect:&visRect];
  290.     NXIntersectionRect(&bounds, &visRect);
  291.     [self addCursorRect:&visRect  cursor:[NXApp  cursor]];
  292.  
  293.     return self;
  294. }
  295.  
  296. /*
  297. *    When the drawing view moves, then move bufferId so that
  298. *    the composites from bufferId are taken from the correct spot.
  299. */
  300. - moveTo:(NXCoord)x :(NXCoord)y
  301. {
  302.     [super moveTo:x :y];
  303.     [bufferId moveTo:x :y];
  304.  
  305.     return self;
  306. }
  307.  
  308. /*
  309. *    A scale as well needs to be reflected in the offscreen buffer.
  310. */
  311. - scale:(NXCoord)x :(NXCoord)y
  312. {
  313.     [super scale:x :y];
  314.     [bufferId scale:x :y];
  315.  
  316.     return self;
  317. }
  318.  
  319. /*
  320. *    This method copies the PostScript code for the graphics and writes it to the
  321. *    stream passed in. Includes the preview image when appropriate.
  322. */
  323. - writePSToStream:(NXStream *) stream
  324. {
  325.     id            nximageId, templist;
  326.  
  327.     NXRect        bbox;
  328.  
  329.     if (stream && [selectedlistId  count])
  330.     {
  331.         nximageId = NULL;
  332.         imageId = NULL;
  333.         templist = graphiclistId;
  334.         graphiclistId = selectedlistId;
  335.         [graphiclistId  getBounds:&bbox];
  336.         if ([[SaveAsPanel  new]  format] == SAVE_EPSPREVIEW)
  337.         {
  338.             nximageId = [[NXImage  alloc]  initSize:&bbox.size];
  339.             [nximageId  useCacheWithDepth:NX_TwoBitGrayDepth];
  340.             if ([nximageId  lockFocus])
  341.             {
  342.                 PStranslate(-bbox.origin.x, -bbox.origin.y);
  343.                 PSsetgray(NX_WHITE);
  344.                 NXRectFill(&bbox);
  345.                 [graphiclistId  drawObject:&bbox  withFlags:NOFLAGS  inView:self];
  346.  
  347.                 imageId = [[NXBitmapImageRep  alloc]  initData:NULL  fromRect:&bbox];
  348.                 [nximageId  unlockFocus];
  349.             }
  350.         }
  351.  
  352.         [self copyPSCodeInside:&bbox to:stream];
  353.  
  354.         selectedlistId = graphiclistId;
  355.         graphiclistId = templist;    
  356.         [nximageId  free];
  357.         [imageId  free];
  358.         imageId = NULL;
  359.     }
  360.  
  361.     return self;
  362. }
  363.  
  364. /*   Pasteboard-related target/action methods */
  365. /*  Calls copy:, then removes and frees the objects in the selection.  */
  366. - cut:sender
  367. {
  368.     float            knobsize;
  369.  
  370.     NXRect        rect;
  371.  
  372.     if ([selectedlistId  count])
  373.     {
  374.         knobsize = -[self  controlPointSize]/2;
  375.         [selectedlistId  getBounds:&rect];
  376.         NXInsetRect(&rect, knobsize, knobsize);
  377.  
  378.         if (sender != self)
  379.             [self  copy:sender];
  380.         [graphiclistId  removeObjectsIn:selectedlistId];
  381.         [selectedlistId  freeObjects];
  382.  
  383.         [self  display:&rect  :1];
  384.         [self  setDirty:YES];
  385.  
  386.         return self;
  387.     }
  388.  
  389.     return nil;
  390. }
  391.  
  392. /*  Calls cut bypassing the copy.  */
  393. - delete:sender
  394. {
  395.     return [self  cut:self];
  396. }
  397.  
  398. /*
  399.  *    Puts all the objects in the selected list into the Pasteboard by
  400.  *    archiving the list. See the draw program for placing the objects
  401.  *    in the Pasteboard as a PostScript image. Much of the work is
  402.  *    available already. The right hooks just have to be added.
  403.  */
  404. - copy:sender
  405. {
  406.     BOOL        error = YES;
  407.  
  408.     id            pasteboardId = [Pasteboard  new];
  409.  
  410.     char            *dataptr;
  411.  
  412.     const char    *types[1];
  413.  
  414.     int            length, maxlen;
  415.  
  416.     NXStream    *stream;   
  417.  
  418.     NXTypedStream *ts;
  419.  
  420.     if ([selectedlistId  count])
  421.     {
  422.         types[0] = EpsfPboard;
  423.  
  424.         stream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
  425.         if (stream)
  426.         {
  427.             ts = NXOpenTypedStream(stream, NX_WRITEONLY);
  428.             if (ts)
  429.             {
  430.                 NXWriteRootObject(ts, selectedlistId);
  431.                 NXCloseTypedStream(ts);
  432.                 NXGetMemoryBuffer(stream, &dataptr, &length, &maxlen);
  433.  
  434.                 [pasteboardId  declareTypes:types  num:2  owner:[self class]];
  435.                 [pasteboardId  writeType:EpsfPboard  data:dataptr  length:length];
  436.                 error = NO;
  437.             }
  438.             NXCloseMemory(stream, NX_FREEBUFFER);
  439.         }
  440.     }
  441.  
  442.     return (error ? nil : self);
  443. }
  444.  
  445. /*    Routines to check the types in the Pasteboard */
  446. static BOOL matchPasteType(char *const *types, const char *type)
  447. {
  448.     if (types)
  449.         while (*types)
  450.             if (!strcmp(*types++, type))
  451.                 return YES;
  452.  
  453.     return NO;
  454. }
  455.  
  456. /*
  457.  *    Validates the pasteboard types and returns the preferred one.
  458.  */
  459. static const char *drawPasteType(char *const *types)
  460. {
  461.     if (matchPasteType(types, EpsfPboard))
  462.         return EpsfPboard;
  463.     if (matchPasteType(types, NXPostScriptPboard))
  464.         return NXPostScriptPboard;
  465.     if (matchPasteType(types, NXTIFFPboardType))
  466.         return NXTIFFPboardType;
  467.  
  468.     return NULL;
  469. }
  470.  
  471.  /*
  472.  *    Pastes any type available from the Pasteboard into the DrawingView.
  473.  *    If the type in the Pasteboard is the internal type, then the objects
  474.  *    are simply added to the selected list and graphic list.  If it is of PostScript
  475.  *    or Tiff type then create a GraphicImport object using the contents of the
  476.  *    Pasteboard.
  477.  */
  478. - paste:sender
  479. {
  480.     id            objectId,
  481.                 pastelistId,
  482.                 pasteboardId = [Pasteboard  new];
  483.  
  484.     char            *dataptr;
  485.  
  486.     const char    *type;
  487.  
  488.     int            length;
  489.  
  490.     float            knobsize;
  491.  
  492.     NXPoint        offset;
  493.  
  494.     NXRect        drawRect, visRect;
  495.  
  496.     NXStream    *stream;
  497.  
  498.     NXTypedStream *ts;
  499.  
  500.     type = drawPasteType([pasteboardId  types]);
  501.     if (type)
  502.     {
  503.         [self  lockFocus];
  504.             [self  deselectObject:selectedlistId];
  505.         [self  unlockFocus];
  506.         [pasteboardId  readType:type data:&dataptr length:&length];
  507.         stream = NXOpenMemory(dataptr, length, NX_READONLY);
  508.         if (strcmp(type, EpsfPboard) == 0)
  509.         {
  510.             ts = NXOpenTypedStream(stream, NX_READONLY);
  511.             pastelistId = NXReadObject(ts);
  512.             if ([pastelistId  count])
  513.             {
  514.                 [graphiclistId  insertObjectsIn:pastelistId];
  515.                 [selectedlistId  insertObjectsIn:pastelistId];
  516.                 [selectedlistId  setSelected:YES];
  517.             }
  518.             NXCloseTypedStream(ts);
  519.         }
  520.         else if (strcmp(type, NXPostScriptPboard) == 0 ||
  521.                 strcmp(type, NXTIFFPboardType) == 0)        {
  522.             objectId = [[GraphicImport alloc]  initFromStream:stream];
  523.             [graphiclistId  addObject:objectId];
  524.             [selectedlistId  addObject:objectId];
  525.             [selectedlistId  setSelected:YES];
  526.         }
  527.         NXCloseMemory(stream, NX_SAVEBUFFER);
  528.         
  529.         if ([selectedlistId  count])
  530.         {
  531.             knobsize = -[self  controlPointSize]/2;
  532.             [selectedlistId  getBounds:&drawRect];
  533.             NXInsetRect(&drawRect, knobsize, knobsize);
  534.  
  535.             [self  getVisibleRect:&visRect];
  536.             offset.x = (visRect.origin.x + visRect.size.width/2.0) -
  537.                         (drawRect.origin.x + drawRect.size.width/2.0);
  538.             offset.y = (visRect.origin.y + visRect.size.height/2.0) -
  539.                         (drawRect.origin.y + drawRect.size.height/2.0);
  540.  
  541.             [selectedlistId  moveAll:&offset];
  542.             drawRect.origin.x += offset.x;
  543.             drawRect.origin.y += offset.y;
  544.             [self  display:&drawRect  :1];
  545.             [self  setDirty:YES];
  546.         }
  547.     }
  548.     else
  549.         return nil;
  550.  
  551.     return self;
  552. }
  553.  
  554. /*
  555.  *    Selects all the items in the graphiclistId.
  556.  */
  557. - selectAll:sender
  558. {
  559.     int        i;
  560.  
  561.     float        knobsize;
  562.  
  563.     NXRect    rect;
  564.  
  565.     i = [graphiclistId  count];
  566.     if (i  && i > [selectedlistId  count])
  567.     {
  568.         [selectedlistId  free];
  569.         selectedlistId = [graphiclistId  copy];
  570.         [selectedlistId  setSelected:YES];
  571.  
  572.         knobsize = -[self  controlPointSize]/2;
  573.         [selectedlistId  getBounds:&rect];
  574.         NXInsetRect(&rect, knobsize, knobsize);
  575.  
  576.         [self  lockFocus];
  577.             [self  drawControl:selectedlistId  forRect:&rect  withFlags:NOFLAGS];
  578.         [self  unlockFocus];
  579.         [window  flushWindow];
  580.        }
  581.  
  582.     return self;
  583. }
  584.  
  585. /*
  586.  *    Brings each of the items in the selected list to the front of the
  587.  *    graphics list. The first item selected will be at the front of the
  588.  *    graphics list with the others following immediately behind.
  589.  */
  590. - bringToFront:sender
  591. {
  592.     id        object;
  593.  
  594.     BOOL    change = NO;
  595.  
  596.     int        i, j, index;
  597.  
  598.     float        knobsize;
  599.  
  600.     NXRect    rect;
  601.  
  602.     j = [selectedlistId  count];
  603.     for (i = 0; i < j; i++)
  604.     {
  605.         object = [selectedlistId  objectAt:i];
  606.         index = [graphiclistId  indexOf:object];
  607.         if (index != i)
  608.         {
  609.             [graphiclistId  removeObjectAt:index];
  610.             [graphiclistId  insertObject:object at:i];
  611.             change = YES;
  612.         }
  613.     }
  614.  
  615.     if (change)
  616.     {
  617.         knobsize = -[self  controlPointSize]/2;
  618.         [selectedlistId  getBounds:&rect];
  619.         NXInsetRect(&rect, knobsize, knobsize);
  620.  
  621.         [self  display:&rect  :1];
  622.         [self  setDirty:YES];
  623.     }
  624.  
  625.     return self;
  626. }
  627.  
  628. /*
  629.  *    Moves each of the items in the selected list to the back of the
  630.  *    graphics list. The last item selected will be at the back of the
  631.  *    graphics list with the others following immediately ahead.
  632.  */
  633. - sendToBack:sender
  634. {
  635.     id        object;
  636.  
  637.     BOOL    change = NO;
  638.  
  639.     int        numS, numG, i, index;
  640.  
  641.     float        knobsize;
  642.  
  643.     NXRect    rect;
  644.  
  645.     numS = [selectedlistId  count] - 1;
  646.     numG = [graphiclistId  count] - 1;
  647.     for (i = numS; i >= 0; i--)
  648.     {
  649.         object = [selectedlistId  objectAt:i];
  650.         index = [graphiclistId  indexOf:object];
  651.         if (numG - index != numS - i)
  652.         {
  653.             [graphiclistId  removeObjectAt:index];
  654.             [graphiclistId  insertObject:object at:(numG - (numS - i))];
  655.             change = YES;
  656.         }
  657.     }
  658.  
  659.     if (change)
  660.     {
  661.         knobsize = -[self  controlPointSize]/2;
  662.         [selectedlistId  getBounds:&rect];
  663.         NXInsetRect(&rect, knobsize, knobsize);
  664.  
  665.         [self  display:&rect  :1];
  666.         [self  setDirty:YES];
  667.     }
  668.  
  669.     return self;
  670. }
  671.  
  672. - makeOriginalUsing:(SEL) aMethod
  673. {
  674.     float            knobsize;
  675.  
  676.     NXRect        rect_start, rect_end;
  677.  
  678.     knobsize = -[self  controlPointSize]/2;
  679.     [selectedlistId  getBounds:&rect_start];
  680.     NXInsetRect(&rect_start, knobsize, knobsize);
  681.  
  682.     if ([selectedlistId  makeObjectsPerform:aMethod])
  683.     {
  684.         [selectedlistId  getBounds:&rect_end];
  685.         NXInsetRect(&rect_end, knobsize, knobsize);
  686.  
  687.         NXUnionRect(&rect_start, &rect_end);
  688.         [self  lockFocus];
  689.             [self  drawSelf:&rect_end  :1];
  690.         [self  unlockFocus];
  691.         [self  setDirty:YES];
  692.         [window  flushWindow];    
  693.     }
  694.  
  695.     return self;
  696. }
  697.  
  698. /*
  699.  *    Sizes the selected objects to have their original sizes.
  700.  */
  701. - originalSize:sender
  702. {
  703.     return [self  makeOriginalUsing:@selector(setOriginalSize)];
  704. }
  705.  
  706. /*
  707.  *    Sizes the selected objects to have the save width/height ratio as their
  708.  *    original bounding boxes. Centers them about their centers.
  709.  */
  710. - originalRatio:sender
  711. {
  712.     return [self  makeOriginalUsing:@selector(setOriginalRatio)];
  713. }
  714.  
  715. /*
  716.  * Scrolls to rectangle passed in if it is not in visible portion of the view.
  717.  * If the rectangle is larger in width or height than the view, the scrollRectToVisible
  718.  * method is not altogether consistent. As a result, the rectangle contains only
  719.  * the image that was previously visible.
  720.  */
  721.  - scrollToRect:(const NXRect *)toRect
  722. {
  723.     NXRect        visRect;
  724.  
  725.     [self getVisibleRect:&visRect];
  726.     if (!NXContainsRect(&visRect, toRect))
  727.     {
  728.         [window disableFlushWindow];
  729.         [self scrollRectToVisible:toRect];
  730.         [window reenableFlushWindow];
  731.  
  732.         startTimer(&timer, &timermask, window);
  733.     }
  734.     else
  735.         stopTimer(&timer, &timermask, window);
  736.  
  737.     return self;
  738. }
  739.  
  740. /*
  741. *    Constrain the point within the view. An offset is needed because when
  742. *    an object is moved, it is often grabbed in the center of the object. If the
  743. *    lower left offset and the upper right offset were not included then part of
  744. *    the object could be moved off of the view. (In some applications, that might
  745. *    be allowed but in this one the object is constrained to always lie in the
  746. *    page.)
  747. */
  748. - constrainPoint:(NXPoint *)aPt  withOffset:(const NXSize*)llOffset  :(const NXSize*)urOffset
  749. {
  750.     float            margin;
  751.  
  752.     NXPoint        viewMin, viewMax;
  753.  
  754.     margin = ceil(FONTSIZE/2);
  755.  
  756.     viewMin.x = bounds.origin.x + llOffset->width + margin;
  757.     viewMin.y = bounds.origin.y + llOffset->height + margin;
  758.  
  759.     viewMax.x = bounds.origin.x + bounds.size.width - urOffset->width - margin;
  760.     viewMax.y = bounds.origin.y + bounds.size.height  - urOffset->height - margin;
  761.  
  762.     aPt->x = MAX(viewMin.x, aPt->x);
  763.     aPt->y = MAX(viewMin.y, aPt->y);
  764.  
  765.     aPt->x = MIN(viewMax.x, aPt->x);    
  766.     aPt->y = MIN(viewMax.y, aPt->y);
  767.  
  768.     return self;
  769. }
  770.  
  771. /*
  772. *    Constrain a rectangle within the view.
  773. */
  774. - constrainRect:(NXRect *)aRect
  775. {
  776.     float            margin;
  777.     
  778.     NXPoint        viewMin, viewMax;
  779.  
  780.     margin = ceil(FONTSIZE/2);
  781.  
  782.     viewMin.x = bounds.origin.x + margin;
  783.     viewMin.y = bounds.origin.y + margin;
  784.  
  785.     viewMax.x = bounds.origin.x + bounds.size.width  - aRect->size.width - margin;
  786.     viewMax.y = bounds.origin.y + bounds.size.height - aRect->size.height - margin;
  787.  
  788.     aRect->origin.x = MAX(viewMin.x, aRect->origin.x);
  789.     aRect->origin.y = MAX(viewMin.y, aRect->origin.y);
  790.  
  791.     aRect->origin.x = MIN(viewMax.x, aRect->origin.x );    
  792.     aRect->origin.y = MIN(viewMax.y, aRect->origin.y);
  793.  
  794.     return self;
  795. }
  796.  
  797. /*
  798.  *    Redraws the graphic. The image from the buffer is composited
  799.  *    into the window and then the changed object is drawn atop the
  800.  *    old image. A copy of the image is necessary because when the
  801.  *    window is scrolled the buffer is also scrolled. When the
  802.  *    buffer is scrolled, the old image might have to be redrawn.
  803.  *    As a result, a copy is created and the changes performed on the
  804.  *    copy.  Care is taken to limit the amount of area that must be
  805.  *    composited and redrawn. A timer is started is the scrolling rect
  806.  *    moves outside the visible portion of the view.
  807.  */
  808. - redrawObject:objectId  :(int) pt_num
  809. {
  810.     id            copyId;
  811.  
  812.     BOOL        tracking,
  813.                 dirtyFlag = NO;
  814.  
  815.     int            old_mask;
  816.     
  817.     float            knobsize;
  818.  
  819.     NXPoint        pt, pt_last, pt_old, delta;
  820.     
  821.     NXRect        rect_now, rect_start, rect_last, rect_scroll, rect_vis;
  822.  
  823.     NXEvent        *event;
  824.  
  825.     timermask = 0;
  826.  
  827.     /*
  828.     *  Create a copy of the selected object. If we scroll we will need to redraw
  829.     *  the old object. If we do not create a copy we will not have an old object.
  830.     *  The copy method copies the stream pointer and not the data
  831.     *  if the imported file happens to be copied into the document.
  832.     */
  833.     copyId = [objectId  copyTemp];
  834.  
  835.     knobsize = -[self  controlPointSize]/2;
  836.     [copyId  getBounds:&rect_start];
  837.     NXInsetRect(&rect_start, knobsize, knobsize);
  838.  
  839.     rect_now = rect_last = rect_start;
  840.  
  841.     /*
  842.     *  The rect_scroll will cause scrolling whenever it goes outside the
  843.     *  visible portion of the view.
  844.     */
  845.     [copyId  getScrollRect:&rect_scroll  forPtNum:pt_num];
  846.     NXInsetRect(&rect_scroll, knobsize, knobsize);
  847.  
  848.     [self  getVisibleRect:&rect_vis];
  849.     NXIntersectionRect(&rect_vis, &rect_scroll);
  850.  
  851.     [copyId  getPoint:pt_num :&pt_last];
  852.     pt_old = pt_last;
  853.  
  854.     old_mask = [window addToEventMask:NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK];
  855.     event = [NXApp getNextEvent:NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK];
  856.     if (event->type != NX_MOUSEUP)
  857.     {
  858.         tracking = YES;
  859.         while (tracking)
  860.         {
  861.             /*
  862.             *  If its a timer event than use the last point. It will be converted to
  863.             *  the view's coordinate so it will appear as a new point.
  864.             */
  865.             if (event->type == NX_TIMER)
  866.                 pt = pt_old;
  867.             else
  868.                 pt = pt_old = event->location;
  869.  
  870.             [self convertPoint:&pt  fromView:nil];
  871.             [copyId constrainPoint:&pt  forPtNum:&pt_num
  872.                     inRect:&bounds  withFlags:event->flags];
  873.  
  874.             delta.x = pt.x - pt_last.x;
  875.             delta.y = pt.y - pt_last.y;
  876.  
  877.             if (delta.x || delta.y)
  878.             {
  879.                 dirtyFlag = YES;
  880.                 /* Change the point location and get the new bounds. */
  881.                 [copyId setPoint:pt_num :&delta];
  882.                 [copyId  getBounds:&rect_now];
  883.                 NXInsetRect(&rect_now, knobsize, knobsize);
  884.  
  885.                 /* Change the scrolling rectangle. */
  886.                 NXOffsetRect(&rect_scroll, delta.x, delta.y);
  887.                 [self  scrollToRect:&rect_scroll];
  888.  
  889.                 /* Composite the old image and then redraw the new one. */
  890.                 compositeBuffer([bufferId gState], &rect_last, &rect_last.origin, NX_COPY);
  891.                 [self  drawObject:copyId  forRect:&rect_now  withFlags:REDRAWFLAG];
  892.                 [self  drawControl:copyId  forRect:&rect_now  withFlags:NOFLAGS];
  893.  
  894.                 /* Sync up the drawing so it proceeds a little smoother. */
  895.                 [window flushWindow];
  896.                 NXPing();
  897.                 
  898.                 rect_last = rect_now;
  899.                 pt_last = pt;
  900.             }
  901.             else
  902.                 stopTimer(&timer, &timermask, window);
  903.  
  904.             event = [NXApp getNextEvent:NX_MOUSEUPMASK|
  905.                             NX_MOUSEDRAGGEDMASK|timermask];
  906.             tracking = (event->type != NX_MOUSEUP);
  907.         }
  908.         stopTimer(&timer, &timermask, window);
  909.     }
  910.     [window setEventMask:old_mask];
  911.  
  912.     if (![graphiclistId  replaceObject:objectId  with:copyId])
  913.         [graphiclistId  insertObject:copyId at:0];
  914.  
  915.     if (![selectedlistId  replaceObject:objectId  with:copyId])
  916.         [selectedlistId  insertObject:copyId at:0];
  917.  
  918.     [objectId  freeTemp];
  919.  
  920.     if (dirtyFlag)
  921.     {
  922.         /*
  923.         *  The view has already been focused and we know what
  924.         *  has to be redrawn so call drawSelf:: instead of display
  925.         */
  926.         NXUnionRect(&rect_last, &rect_start);
  927.         [self drawSelf:&rect_start :1];
  928.         [window flushWindow];
  929.         NXPing();
  930.         [self  setDirty:YES];
  931.     }
  932.  
  933.     return self;
  934. }
  935.  
  936. /*
  937.  *    Moves the selected objects by performing a translate before drawing
  938.  *    the objects. This approach is used because the objects are drawn
  939.  *    into windows and drawing simply means compositing the windows.
  940.  *
  941.  *    The offsets constrain the selected object to stay within the dimensions
  942.  *    of the view. 
  943.  */
  944. - moveObject:(NXEvent *)event
  945. {
  946.     BOOL        tracking,
  947.                 moveFlag = NO,
  948.                 dirtyFlag = NO;
  949.     
  950.     int            old_mask;
  951.  
  952.     long            time;
  953.  
  954.     float            knobsize;
  955.  
  956.     NXSize        llOffset, urOffset;
  957.  
  958.     NXPoint        pt, pt_last, pt_old, delta, delta_scroll;
  959.  
  960.     NXRect        rect_now, rect_start, rect_last, rect_scroll, rect_vis;
  961.  
  962.     /*
  963.     *  Get the scrolling rectangle. Compare it against a reduced version of 
  964.     *  the visible rect. If it turns out to be the larger then use the reduced
  965.     *  rectangle so that the user is not playing pong when trying to
  966.     *  move the image.
  967.     */
  968.     knobsize = -[self  controlPointSize]/2;
  969.     [selectedlistId  getScrollRect:&rect_scroll  forPtNum:-1];
  970.     NXInsetRect(&rect_scroll, knobsize, knobsize);
  971.  
  972.     [self  getVisibleRect:&rect_vis];
  973.     NXInsetRect(&rect_vis, rect_vis.size.width * 0.20, rect_vis.size.height * 0.20);
  974.     if (rect_scroll.size.width > rect_vis.size.width || rect_scroll.size.height > rect_vis.size.height) 
  975.     {
  976.         NXInsetRect(&rect_scroll, MAX(0, (rect_scroll.size.width - rect_vis.size.width)/2),
  977.                 MAX(0, (rect_scroll.size.height - rect_vis.size.height)/2));
  978.     }
  979.  
  980.     [selectedlistId  getBounds:&rect_start];
  981.     NXInsetRect(&rect_start, knobsize, knobsize);
  982.     rect_now = rect_last = rect_start;
  983.  
  984.     delta_scroll.x = rect_scroll.origin.x - rect_now.origin.x;
  985.     delta_scroll.y = rect_scroll.origin.y - rect_now.origin.y;
  986.  
  987.     timermask = 0;
  988.     time = event->time;
  989.     pt_last = pt_old = event->location;
  990.     [self  convertPoint:&pt_last  fromView:nil];
  991.     
  992.     /* Calculate where the mouse point falls relative to the object. */
  993.     llOffset.width = pt_last.x - rect_start.origin.x;
  994.     llOffset.height = pt_last.y - rect_start.origin.y;    
  995.     urOffset.width = rect_start.origin.x + rect_start.size.width - pt_last.x;
  996.     urOffset.height = rect_start.origin.y + rect_start.size.height - pt_last.y;
  997.  
  998.     /* Return nil if the the mouse was not dragged. */
  999.     old_mask = [window addToEventMask:NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK];
  1000.     event = [NXApp getNextEvent:NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK];
  1001.     if (event->type != NX_MOUSEUP)
  1002.     {
  1003.         tracking = YES;
  1004.         while (tracking)
  1005.         {            
  1006.             /*
  1007.             *    Only move the object if a certain amount of time has elapsed
  1008.             *    between mouse down and mouse up. Prevents accidently
  1009.             *    moving objects when selecting.
  1010.             */
  1011.             if (event->time - time > MOVE_INTERVAL)
  1012.             {
  1013.                 moveFlag = YES;
  1014.             if (event->type == NX_TIMER)
  1015.                 pt = pt_old;
  1016.             else
  1017.                 pt = pt_old = event->location;
  1018.                 
  1019.             [self  convertPoint:&pt fromView:nil];
  1020.             [self  constrainPoint:&pt  withOffset:&llOffset :&urOffset];
  1021.             [self  constrainPoint:&pt_last  withOffset:&llOffset :&urOffset];
  1022.             delta.x = pt.x - pt_last.x;
  1023.             delta.y = pt.y - pt_last.y;
  1024.  
  1025.             if (delta.x || delta.y)
  1026.             {
  1027.                 dirtyFlag = YES;
  1028.                 NXOffsetRect(&rect_now, delta.x, delta.y);
  1029.                 [self  constrainRect:&rect_now];
  1030.  
  1031.                 rect_scroll.origin.x = rect_now.origin.x + delta_scroll.x;
  1032.                 rect_scroll.origin.y = rect_now.origin.y + delta_scroll.y;
  1033.                 [self  scrollToRect:&rect_scroll];
  1034.  
  1035.                 /*
  1036.                 *  Composite the old image into the window and then
  1037.                 *  translate the user space and draw the objects.
  1038.                 */
  1039.                 compositeBuffer([bufferId gState], &rect_last, &rect_last.origin, NX_COPY);
  1040.                 PSgsave();
  1041.                     PStranslate(rect_now.origin.x - rect_start.origin.x,
  1042.                         rect_now.origin.y - rect_start.origin.y);
  1043.                     [self  drawObject:selectedlistId  forRect:&rect_start  withFlags:MOVEFLAG];
  1044.                     [self  drawControl:selectedlistId  forRect:&rect_start 
  1045.                             withFlags:NOFLAGS];
  1046.                 PSgrestore();
  1047.  
  1048.                 [window flushWindow];
  1049.                 NXPing();
  1050.                 
  1051.                 rect_last = rect_now;
  1052.                 pt_last = pt;
  1053.             }
  1054.             else
  1055.                 stopTimer(&timer, &timermask, window);
  1056.             }
  1057.             event = [NXApp getNextEvent:NX_MOUSEUPMASK|
  1058.                         NX_MOUSEDRAGGEDMASK|timermask];
  1059.  
  1060.             tracking = (event->type != NX_MOUSEUP);
  1061.         }
  1062.         stopTimer(&timer, &timermask, window);
  1063.  
  1064.         if (dirtyFlag)
  1065.         {
  1066.             delta.x = rect_now.origin.x - rect_start.origin.x;
  1067.             delta.y = rect_now.origin.y - rect_start.origin.y;        
  1068.             [selectedlistId  moveAll:&delta];
  1069.     
  1070.             /*
  1071.             *  The view has already been focused and we know what
  1072.             *  has to be redrawn so call drawSelf:: instead of display
  1073.             */
  1074.             NXUnionRect(&rect_now, &rect_start);
  1075.             [self drawSelf:&rect_start :1];
  1076.             [window flushWindow];
  1077.             NXPing();
  1078.             [self  setDirty:YES];
  1079.         }
  1080.     }
  1081.  
  1082.     [window setEventMask:old_mask];
  1083.  
  1084.     /*
  1085.     *  Return nil if the elapsed time is less than the amount necessary to
  1086.     *  consider the action a move.
  1087.     */
  1088.     if (moveFlag)
  1089.         return self;
  1090.     else
  1091.         return nil;
  1092. }
  1093.  
  1094. -eraseRotatePoint:(NXRect *) drawnRect
  1095. {
  1096.     float            scale;
  1097.  
  1098.     NXRect        rotateRect;
  1099.  
  1100.     scale = 1.0 / [superview  scale];
  1101.     rotateRect.origin.x = rotatePoint.x - 8.0 * scale;
  1102.     rotateRect.origin.y = rotatePoint.y - 8.0 * scale;
  1103.     rotateRect.size.width = rotateRect.size.height = 16.0 * scale;
  1104.     if (!NXContainsRect(drawnRect, &rotateRect))
  1105.     {
  1106.         compositeBuffer([bufferId gState], &rotateRect, &rotateRect.origin, NX_COPY);
  1107.         [self  drawControl:selectedlistId  forRect:&rotateRect  withFlags:NOFLAGS];
  1108.     }
  1109.  
  1110.     return self;
  1111. }
  1112.  
  1113. -drawRotatePoint
  1114. {
  1115.     float            scale;
  1116.  
  1117.     NXPoint        compPt;
  1118.  
  1119.     scale = 1.0 / [superview  scale];
  1120.     compPt.x = rotatePoint.x - 8.0 * scale;
  1121.     compPt.y = rotatePoint.y - 8.0 * scale;
  1122.     [[[NXApp  getCursor:OP_ROTATE1]  image] composite:NX_SOVER  toPoint:&compPt];
  1123.  
  1124.     return self;
  1125. }
  1126.  
  1127. /*
  1128. *    Mark the point about which to rotate. Employ gravity near any
  1129. *    control points.    
  1130. */
  1131. - rotateObjectStart:(NXEvent *)event
  1132. {
  1133.     id            objectId;
  1134.  
  1135.     int            pt_num;
  1136.  
  1137.      rotatePoint = event->location; 
  1138.     [self convertPoint:&rotatePoint  fromView:nil];
  1139.     [self  lockFocus];
  1140.         if (objectId = [self  checkControl:&rotatePoint :&pt_num])
  1141.             [objectId  getPoint:pt_num  :&rotatePoint];
  1142.         [self  drawRotatePoint];
  1143.     [self  unlockFocus];
  1144.     [window  flushWindow];    
  1145.  
  1146.     return self;
  1147. }
  1148.  
  1149. /*
  1150.  *    Rotates the selected graphics about the rotatePoint selected
  1151.  *    from the previous mouse down. The image from the buffer
  1152.  *    is composited into the window and then the rotated object is
  1153.  *    drawn atop the old image. 
  1154.  *
  1155.  *    Care is taken to limit the amount of area that must be
  1156.  *    composited and redrawn. A timer is started is the scrolling rect
  1157.  *    moves outside the visible portion of the view.
  1158.  */
  1159. - rotateObject:(NXEvent *)event
  1160. {
  1161.     id            slist;
  1162.  
  1163.     BOOL        tracking,
  1164.                 dirtyFlag = NO;
  1165.  
  1166.     int            old_mask;
  1167.  
  1168.     float            knobsize, marginsize;
  1169.     
  1170.     float            radians_start, radians_last, radians_delta;
  1171.  
  1172.     NXPoint        pt, pt_old, d;
  1173.     
  1174.     NXRect        rect_now, rect_start, rect_last, rect_scroll;
  1175.  
  1176.     slist = [selectedlistId  copyTemp];
  1177.  
  1178.     timermask = 0;
  1179.     marginsize = (1.0/[superview  scale] * SCROLL_MARGIN)/2;
  1180.      rect_scroll.size.width = rect_scroll.size.height = marginsize * 2;
  1181.  
  1182.     knobsize = -[self  controlPointSize]/2;
  1183.     [slist  getBounds:&rect_start];
  1184.     NXInsetRect(&rect_start, knobsize, knobsize);
  1185.  
  1186.     rect_now = rect_last = rect_start;
  1187.  
  1188.     pt_old = pt = event->location;
  1189.     [self convertPoint:&pt fromView:nil];
  1190.  
  1191.     d.x = pt.x - rotatePoint.x; 
  1192.     d.y = pt.y - rotatePoint.y;
  1193.  
  1194.     radians_start = 0;
  1195.     if (d.x || d.y)
  1196.         radians_start = atan2(d.y,d.x);
  1197.     radians_last = radians_start;
  1198.  
  1199.     [self  lockFocus];
  1200.     old_mask = [window addToEventMask:NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK];
  1201.     event = [NXApp getNextEvent:NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK];
  1202.     if (event->type != NX_MOUSEUP)
  1203.     {
  1204.         tracking = YES;
  1205.         while (tracking)
  1206.         {
  1207.             /*
  1208.             *  If its a timer event than use the last point. It will be converted to
  1209.             *  the view's coordinate so it will appear as a new point.
  1210.             */
  1211.             if (event->type == NX_TIMER)
  1212.                 pt = pt_old;
  1213.             else
  1214.                 pt = pt_old = event->location;
  1215.  
  1216.             [self  convertPoint:&pt  fromView:nil];
  1217.  
  1218.             d.x = pt.x - rotatePoint.x; 
  1219.             d.y = pt.y - rotatePoint.y;
  1220.             if (d.x || d.y)
  1221.             {
  1222.                 dirtyFlag = YES;
  1223.                 radians_delta = atan2(d.y, d.x) - radians_last;
  1224.                 [slist constrainAngle:&radians_delta  withFlags:event->flags];
  1225.  
  1226.                 /* Rotate the object about the given point at the given angle. */
  1227.                 [slist  rotateAboutPoint:&rotatePoint  withAngle:radians_delta];
  1228.                 [slist  getBounds:&rect_now];
  1229.                 NXInsetRect(&rect_now, knobsize, knobsize);
  1230.  
  1231.                 /* Make the scrolling rectangle the mouse point with a little margin. */
  1232.                 rect_scroll.origin.x = pt.x - marginsize;
  1233.                 rect_scroll.origin.y = pt.y - marginsize;
  1234.                 [self  scrollToRect:&rect_scroll];
  1235.  
  1236.                 /* Composite the old image and then redraw the new one. */
  1237.                 NXIntegralRect(&rect_last);
  1238.                 compositeBuffer([bufferId gState], &rect_last, &rect_last.origin, NX_COPY);
  1239.                 [self  drawObject:slist  forRect:&rect_now  withFlags:REDRAWFLAG];
  1240.                 [self  drawControl:slist  forRect:&rect_now  withFlags:NOFLAGS];
  1241.                 [self  drawRotatePoint];
  1242.  
  1243.                 /* Sync up the drawing so it proceeds a little smoother. */
  1244.                 [window flushWindow];
  1245.                 NXPing();
  1246.                 
  1247.                 rect_last = rect_now;
  1248.                 radians_last = radians_delta + radians_last;
  1249.             }
  1250.             else
  1251.                 stopTimer(&timer, &timermask, window);
  1252.  
  1253.             event = [NXApp getNextEvent:NX_MOUSEUPMASK|
  1254.                             NX_MOUSEDRAGGEDMASK|timermask];
  1255.             tracking = (event->type != NX_MOUSEUP);
  1256.         }
  1257.         stopTimer(&timer, &timermask, window);
  1258.     }
  1259.     [window setEventMask:old_mask];
  1260.          
  1261.     [graphiclistId  replaceObjectsIn:selectedlistId  with:slist];
  1262.  
  1263.      [selectedlistId  freeTemp];
  1264.      [selectedlistId  free];
  1265.     selectedlistId = slist;
  1266.  
  1267.     if (dirtyFlag)
  1268.     {
  1269.         /*
  1270.         *  The view has already been focused and we know what
  1271.         *  has to be redrawn so call drawSelf:: instead of display
  1272.         */
  1273.         NXUnionRect(&rect_last, &rect_start);
  1274.         [self  drawSelf:&rect_start :1];
  1275.         [self  eraseRotatePoint:&rect_start];
  1276.         [window flushWindow];
  1277.         NXPing();
  1278.         [self  setDirty:YES];
  1279.     }
  1280.     [self  unlockFocus];
  1281.  
  1282.     return self;
  1283. }
  1284.  
  1285. /* Check to see whether a control point has been hit. */
  1286.  - checkControl:(const NXPoint *) p :(int *) pt_num
  1287.  {
  1288.     float        hitsetting;
  1289.  
  1290.     NXRect    hitRect;
  1291.  
  1292.     hitsetting = [self  hitSetting];    
  1293.     NXSetRect(&hitRect, p->x - hitsetting/2, p->y - hitsetting/2, hitsetting, hitsetting);
  1294.  
  1295.     return [selectedlistId  hitControl:&hitRect :pt_num  forSize:[self  controlPointSize]];
  1296.  }
  1297.  
  1298. /* 
  1299. *    Check to see whether an object has been hit. Return the selected list if
  1300. *    any object in it has been hit. Otherwise just return the individual
  1301. *    graphic.
  1302. */
  1303. - checkObject:(const NXPoint *) p  in:listId  from:(int) start  to:(int) end
  1304.  {
  1305.     id        objectId;
  1306.  
  1307.     BOOL    flag;
  1308.  
  1309.     int        i;
  1310.  
  1311.     float        hitsetting;
  1312.  
  1313.     hitsetting = [self  hitSetting];    
  1314.  
  1315.     /*  Bounding Box */
  1316.     hitPoint->pts[0] = floor(p->x - hitsetting/2);
  1317.     hitPoint->pts[1] = floor(p->y - hitsetting/2);
  1318.     hitPoint->pts[2] = ceil(p->x + hitsetting/2);
  1319.     hitPoint->pts[3] = ceil(p->y + hitsetting/2);
  1320.     
  1321.     /*  Moveto */
  1322.     hitPoint->pts[4] = p->x - hitsetting/2;
  1323.     hitPoint->pts[5] = p->y - hitsetting/2;
  1324.  
  1325.     /* Rlineto's */
  1326.     hitPoint->pts[7] = hitsetting;
  1327.     hitPoint->pts[8] = hitsetting;
  1328.     hitPoint->pts[11] = -hitsetting;
  1329.  
  1330.     flag = (listId == graphiclistId);
  1331.     for (i = start; i < end; i++)
  1332.         if (objectId = [[listId  objectAt:i]  hitObject:hitPoint  ifNotSelected:flag])
  1333.             return objectId;
  1334.  
  1335.     return nil;
  1336.  }
  1337.  
  1338.  /*
  1339.  *    Set the object as selected, add the object to the selected list and
  1340.  *    then draw the control points.
  1341.  */
  1342.  - selectObject:objectId
  1343.  {
  1344.     NXRect        drawRect;
  1345.  
  1346.     [objectId  setSelected:YES];
  1347.     [selectedlistId  addObject:objectId];
  1348.  
  1349.     [self  getVisibleRect:&drawRect];
  1350.     [self  drawControl:objectId  forRect:&drawRect  withFlags:NOFLAGS];
  1351.     [window  flushWindow];
  1352.  
  1353.     return self;
  1354. }
  1355.  
  1356. /*
  1357. *    Remove the object (or objects) from the selected list and redraw the
  1358. *    control points for the visible portion. The control points are drawn
  1359. *    in this view since they can be drawn pretty quickly (and so the don't
  1360. *    need to be cached in an offscreen buffer somewhere).
  1361. */
  1362. - deselectObject:objectId
  1363. {
  1364.     NXRect        drawRect;
  1365.  
  1366.     [self  getVisibleRect:&drawRect];
  1367.  
  1368.     [objectId  setSelected:NO];
  1369.     if (objectId == selectedlistId)
  1370.         [selectedlistId  empty];
  1371.     else
  1372.         [selectedlistId  removeObject:objectId];
  1373.  
  1374.     [self  drawControl:selectedlistId  forRect:&drawRect  withFlags:CLEARFLAG];
  1375.     [window  flushWindow];
  1376.     
  1377.     return self;
  1378. }
  1379.  
  1380. /*
  1381. *    Test for a mouse down hit on either the object or the control points.
  1382. *    This algorithm looks further into the list in order to check for mouse
  1383. *    hits on objects that lie below the currently selected object.
  1384. */ 
  1385. - testObject:(NXEvent *)event
  1386. {
  1387.     id            objectId, newobjectId;
  1388.  
  1389.     int            pt_num, list_index;
  1390.     
  1391.     NXPoint        p;
  1392.  
  1393.      p = event->location; 
  1394.     [self convertPoint:&p fromView:nil];
  1395.     [self  lockFocus];
  1396.         if (objectId = [self checkControl:&p :&pt_num])
  1397.         {
  1398.             [self  redrawObject:objectId  :pt_num];
  1399.         }    
  1400.         else
  1401.         {
  1402.             if ([selectedlistId  count] == 0 ||
  1403.                 (event->flags & NX_SHIFTMASK) == NX_SHIFTMASK)
  1404.             {
  1405.                 if (objectId = [self checkObject:&p  in:selectedlistId
  1406.                             from:0  to:[selectedlistId  count]])
  1407.                     [self  deselectObject:objectId];
  1408.                 else  if (objectId = [self checkObject:&p  in:graphiclistId
  1409.                             from:0  to:[graphiclistId  count]])
  1410.                     [self  selectObject:objectId];
  1411.             }
  1412.             else
  1413.             {
  1414.                  objectId = [self checkObject:&p  in:selectedlistId
  1415.                             from:0  to:[selectedlistId  count]];
  1416.                 if (!objectId || ![self  moveObject:event])
  1417.                 {
  1418.                     /*    Look further into the list first.    */
  1419.                     list_index = 0;
  1420.                     if (objectId)
  1421.                         list_index = [graphiclistId  indexOf:objectId];
  1422.                     if ((newobjectId = [self  checkObject:&p  in:graphiclistId
  1423.                             from:list_index  to:[graphiclistId  count]]) ||
  1424.                          (newobjectId = [self  checkObject:&p  in:graphiclistId
  1425.                                  from:0  to:list_index]))
  1426.                     {
  1427.                         [self  deselectObject:selectedlistId];
  1428.                         [self  selectObject:newobjectId];
  1429.                     }
  1430.                     else if (!objectId)        
  1431.                         [self  deselectObject:selectedlistId];
  1432.                 }
  1433.             }
  1434.         }
  1435.     [self  unlockFocus];    
  1436.  
  1437.     return self;
  1438. }
  1439.  
  1440. /*
  1441.  *    Free any previously unplaced imported object first. Next,
  1442.  *    pass the file name to the factory Tiff or Epsf object to create a new
  1443.  *    instance. If successful then remove items from the selected list.
  1444.  *
  1445.  *    If a point has been passed in then place the imported file at its
  1446.  *    original size with its left corner corresponding to p. If not
  1447.  *    then set the NXApp operation to OP_PLACE. The next mouse
  1448.  *    down will begin the modal loop for drawing out the sizing rectangle.
  1449.  */
  1450. - importFile:(const char *) file  at:(NXPoint *) p
  1451. {
  1452.     BOOL        ok;
  1453.  
  1454.     char            *end;
  1455.  
  1456.     NXStream    *stream;
  1457.  
  1458.     ok = NO;
  1459.     [graphicId  free];
  1460.     graphicId = NULL;
  1461.     if (file)
  1462.     {
  1463.         end = strrchr(file, '.');
  1464.         if (end)
  1465.         {
  1466.             if ([[ImportPanel  new]  format]  ==  IMPORT_COPY)
  1467.             {
  1468.                 stream = NXMapFile(file, NX_READONLY);
  1469.                 if (stream)
  1470.                 {
  1471.                     if (strncmp(end, ".tiff", 5) == 0 ||
  1472.                         strncmp(end, ".ps", 3) == 0 ||
  1473.                             strncmp(end, ".eps", 4) == 0)
  1474.                     {
  1475.                          graphicId = [[GraphicImport alloc]  initFromStream:stream];
  1476.                         [graphicId  setFilename:file];
  1477.                     }
  1478.                     NXCloseMemory(stream, NX_SAVEBUFFER);
  1479.                 }
  1480.                 else
  1481.                     Notify("Import Error", "Unable to open file.");
  1482.             }
  1483.             else
  1484.             {
  1485.                 if (strncmp(end, ".tiff", 5) == 0 ||
  1486.                     strncmp(end, ".ps", 3) == 0 ||
  1487.                         strncmp(end, ".eps", 4) == 0)
  1488.                 {
  1489.                      graphicId = [[GraphicImport alloc]  initFromFile:file];
  1490.                 }
  1491.             }
  1492.  
  1493.             if (graphicId)
  1494.             {
  1495.                 [self  lockFocus];
  1496.                     [self  deselectObject:selectedlistId];
  1497.                 [self  unlockFocus];
  1498.  
  1499.                 if (p)
  1500.                 {
  1501.                     [self  placeObjectAt:p];
  1502.                     graphicId = NULL;
  1503.                 }
  1504.                 else
  1505.                     [NXApp  setOperation:OP_IMPORT];
  1506.         
  1507.                 [window  flushWindow];
  1508.                 NXPing();
  1509.                 ok = YES;
  1510.             }
  1511.         }
  1512.         else
  1513.             Notify("Import Error", "Unable to import file. Unrecognized file type.");
  1514.     }
  1515.  
  1516.     return ok ? self : nil;
  1517. }
  1518.  
  1519. /*
  1520.  *    Place the graphicId with its upper left corner at p;
  1521.  */
  1522. - placeObjectAt:(const NXPoint *) p
  1523. {
  1524.     float            knobsize;
  1525.  
  1526.     NXPoint        pt;
  1527.  
  1528.     NXRect        rect_draw;
  1529.  
  1530.     if (graphicId)
  1531.     {
  1532.         pt = *p;
  1533.         [graphiclistId  insertObject:graphicId at:0];
  1534.         [selectedlistId  insertObject:graphicId at:0];
  1535.  
  1536.         [graphicId  getBounds:&rect_draw];
  1537.         pt.y = pt.y - rect_draw.size.height;
  1538.         [graphicId  setOrigin:&pt];
  1539.  
  1540.         knobsize = -[self  controlPointSize]/2;
  1541.         [graphicId  getBounds:&rect_draw];
  1542.         NXInsetRect(&rect_draw, knobsize, knobsize);
  1543.  
  1544.         [self  display:&rect_draw :1];
  1545.     }
  1546.  
  1547.     return self;
  1548. }
  1549.  
  1550. /*
  1551.  *    Begins the setup for placing an imported file into the document.
  1552.  *    The object for the file is created and then waits for the mouse
  1553.  *    down to begin placement and sizing. This method first gets the
  1554.  *    object and then messages the redrawObject method to draw
  1555.  *    the subsequent sizing rectangles.
  1556.  */
  1557. - importObject:(NXEvent *)event
  1558. {
  1559.     float            knobsize;
  1560.  
  1561.     NXPoint        p;
  1562.  
  1563.     NXRect        rect_draw;
  1564.  
  1565.      if (graphicId)
  1566.     {
  1567.         p = event->location; 
  1568.         [self convertPoint:&p fromView:nil];
  1569.         if ([[ImportPanel  new]  dragToSize])
  1570.         {
  1571.             [NXApp  setOperation:OP_PLACE];
  1572.  
  1573.             NXSetRect(&rect_draw, p.x, p.y-SIZE_MIN, SIZE_MIN, SIZE_MIN);
  1574.             [graphicId  setBounds:&rect_draw];
  1575.  
  1576.             knobsize = -[self  controlPointSize]/2;
  1577.             NXInsetRect(&rect_draw, knobsize, knobsize);
  1578.  
  1579.             [self  lockFocus];
  1580.                 [self  drawObject:graphicId  forRect:&rect_draw  withFlags:REDRAWFLAG];
  1581.                 [self  drawControl:graphicId  forRect:&rect_draw  withFlags:NOFLAGS];
  1582.                 [self  redrawObject:graphicId  :8];
  1583.             [self  unlockFocus];
  1584.  
  1585.             graphicId = [selectedlistId  objectAt:0];
  1586.         }
  1587.         else
  1588.             [self  placeObjectAt:&p];
  1589.  
  1590.         if ([graphicId  error])
  1591.         {
  1592.             [self  lockFocus];
  1593.                 [self  deselectObject:graphicId];
  1594.             [self  unlockFocus];
  1595.             [graphiclistId  removeObject:graphicId];
  1596.             [graphicId  free];
  1597.         }
  1598.         else
  1599.             [self  setDirty:YES];
  1600.         graphicId = NULL;
  1601.     }
  1602.  
  1603.     return self;
  1604. }
  1605.  
  1606.  /*
  1607.  *    Depending on the current operation, check for selection, zoom or
  1608.  *    import a file.
  1609.  */
  1610. - mouseDown:(NXEvent *)event
  1611. {
  1612.     int        operation;
  1613.  
  1614.     operation = [NXApp  operation];
  1615.     switch (operation)
  1616.     {
  1617.         case  OP_SELECT:
  1618.             [self  testObject:event];
  1619.             break;
  1620.         case  OP_ZOOMUP:
  1621.         case  OP_ZOOMDOWN:
  1622.             [nextResponder  scaleDrawView:self  withEvent:event];
  1623.             break;
  1624.         case  OP_ROTATE1:
  1625.             [self  rotateObjectStart:event];
  1626.             [NXApp  setOperation:OP_ROTATE2];
  1627.             break;
  1628.         case  OP_ROTATE2:
  1629.             [self  rotateObject:event];
  1630.             [NXApp  setOperation:OP_ROTATE1];
  1631.             break;
  1632.         case  OP_IMPORT:
  1633.             [self  importObject:event];
  1634.             [NXApp  clearOperation];
  1635.             break;
  1636.     }
  1637.  
  1638.     return self;
  1639. }
  1640.  
  1641. /*
  1642.  *    Changes the cursor to the reduce cursor if the shift key is
  1643.  *    pressed when zooming.
  1644.  */
  1645. - flagsChanged:(NXEvent *) event
  1646. {
  1647.     BOOL    shift;
  1648.  
  1649.     int        operation;
  1650.  
  1651.     operation = [NXApp  operation];
  1652.     shift = (event->flags & NX_ALTERNATEMASK) == NX_ALTERNATEMASK;
  1653.  
  1654.     if (operation == OP_ZOOMUP && shift)
  1655.         [NXApp  setOperation:OP_ZOOMDOWN];
  1656.     else if (operation == OP_ZOOMDOWN && !shift)    
  1657.         [NXApp  setOperation:OP_ZOOMUP];
  1658.     else
  1659.         return [super  flagsChanged:event];
  1660.  
  1661.     return self;
  1662. }
  1663.  
  1664. /*
  1665.  *    Deletes the selected objects.
  1666.  */
  1667. - keyDown:(NXEvent *) event
  1668. {
  1669.     if ([NXApp  operation] == OP_SELECT && 
  1670.          event->data.key.charSet == NX_ASCIISET &&
  1671.          event->data.key.charCode == NX_DELETE)
  1672.         return [self  cut:self];
  1673.     else
  1674.         return nil;
  1675. }
  1676.  
  1677. /*
  1678. *    Draw the control points using the user path buffer to hold
  1679. *    the data for the xyshow. Having the object fill in the data 
  1680. *    structure allows for combining the control points for multiple
  1681. *    objects. This increases drawing performance by reducing
  1682. *    the number of operations. But since this operation is performed
  1683. *    after the objects have been drawn, the control points are above
  1684. *    the graphics. This approach may or may not be desired.
  1685. */
  1686. - drawControl:object  forRect:(NXRect *)r  withFlags:(int)flags
  1687. {
  1688.     float            knobsize;
  1689.  
  1690.     NXPoint        lastpoint;
  1691.     
  1692.     NXRect        rect;
  1693.  
  1694.     if (r)
  1695.         rect = *r;
  1696.     else
  1697.         [self  getVisibleRect:&rect];
  1698.  
  1699.     if (flags & CLEARFLAG)
  1700.         compositeBuffer([bufferId gState], &rect, &rect.origin, NX_COPY);
  1701.  
  1702.     lastpoint.x = 0;
  1703.     lastpoint.y = 0;
  1704.  
  1705.     upathBuffer->num_ops = 0;
  1706.     upathBuffer->num_pts = 0;
  1707.  
  1708.     knobsize = [self  controlPointSize];
  1709.     NXInsetRect(&rect, -knobsize/2, -knobsize/2);
  1710.     [object  putControlPoints:upathBuffer  forRect:&rect  :&lastpoint];
  1711.  
  1712.     upathBuffer->ops[upathBuffer->num_ops] = 0;
  1713.     upathBuffer->pts[upathBuffer->num_pts] = 0;
  1714.     upathBuffer->pts[upathBuffer->num_pts + 1] = 0;
  1715.  
  1716.     if (upathBuffer->num_ops > 0)
  1717.     {
  1718.         PSWSetControlPoints(ControlFont, knobsize, NX_BLACK, 0.15); 
  1719.         PSWDrawControlPoints(upathBuffer->pts[0], upathBuffer->pts[1],
  1720.             &upathBuffer->pts[2], upathBuffer->num_pts, upathBuffer->ops);
  1721.     }
  1722.  
  1723.     return self;
  1724. }
  1725.  
  1726. - drawObject:object  forRect:(NXRect *)r  withFlags:(int) flags
  1727. {
  1728.     NXRect        rect;
  1729.  
  1730.     if (r)
  1731.         rect = *r;
  1732.     else
  1733.         [self  getVisibleRect:&rect];
  1734.  
  1735.     [object  drawObject:&rect  withFlags:flags  inView:self];
  1736.  
  1737.     return self;
  1738. }
  1739.  
  1740. /*
  1741. *    Fill in the background of the rectangle and then draw the graphics.
  1742. *    If NX_DRAWING, then draw into the buffer and composite into
  1743. *    this view's window.
  1744. */
  1745. - drawSelf:(NXRect *)r :(int) count
  1746. {    
  1747.     if (NXDrawingStatus == NX_DRAWING)
  1748.     {
  1749.         [bufferId  lockFocus];
  1750.         PSsetgray(NX_WHITE);
  1751.         NXRectFill(r);
  1752.  
  1753.         [bufferId  unlockFocus];
  1754.         compositeBuffer([bufferId gState], r, &r->origin, NX_COPY);
  1755.         [bufferId  lockFocus];
  1756.     }
  1757.  
  1758.     [self  drawObject:graphiclistId  forRect:r  withFlags:REFRESHFLAG];
  1759.  
  1760.     if (NXDrawingStatus == NX_DRAWING)
  1761.     {
  1762.         [bufferId  unlockFocus];
  1763.         compositeBuffer([bufferId gState], r, &r->origin, NX_COPY);
  1764.         [self  drawControl:selectedlistId  forRect:r  withFlags:NOFLAGS];
  1765.     }
  1766.     
  1767.     return self;            
  1768. }
  1769.  
  1770. /*
  1771. *    This method is only overridden to eliminate during a copy
  1772. *    the rectclip and gsave/grestore pairing that results from
  1773. *    a lockFocus. These are usually harmless operations but
  1774. *    they interfere with trying to produce Illustrator format files.
  1775. */
  1776. - display:(NXRect *)r  :(int) count  :(BOOL)flag
  1777. {
  1778.     if (NXDrawingStatus == NX_COPYING)
  1779.     {
  1780.         [self  drawSelf:r  :count];
  1781.         DPSFlushContext(DPSGetCurrentContext());
  1782.     }
  1783.     else
  1784.         [super  display:r  :count  :flag];
  1785.  
  1786.     return self;
  1787. }
  1788.  
  1789. /*
  1790. *    Do not focus if Printing because we don't want the
  1791. *    scale factored in.
  1792. */
  1793. - (BOOL) lockFocus
  1794. {
  1795.     if (NXDrawingStatus != NX_PRINTING)
  1796.         return [super lockFocus];
  1797.  
  1798.     return YES;
  1799. }
  1800.  
  1801. - unlockFocus
  1802. {
  1803.     if (NXDrawingStatus != NX_PRINTING)
  1804.         return [super unlockFocus];
  1805.  
  1806.     return self;
  1807. }
  1808.  
  1809. - (BOOL)acceptsFirstResponder
  1810. {
  1811.     return YES;
  1812. }
  1813.  
  1814. - write:(NXTypedStream *)stream
  1815. {
  1816.     [super write:stream];
  1817.     NXWriteTypes(stream, "@", &graphiclistId);
  1818.  
  1819.     return self;
  1820. }
  1821.  
  1822. - read:(NXTypedStream *)stream
  1823. {
  1824.     [super read:stream];
  1825.     NXReadTypes(stream, "@", &graphiclistId);
  1826.  
  1827.     return self;
  1828. }
  1829.  
  1830. - awake
  1831. {
  1832.     selectedlistId = [GraphicList  new];
  1833.  
  1834.     hitPoint = [NXApp  hitPoint];
  1835.     upathBuffer = [NXApp upathBuffer];
  1836.  
  1837.     bufferId = createBuffer(&DefaultContentRect.size);
  1838.  
  1839.     return self;
  1840. }
  1841.  
  1842. /*
  1843.  *    Validates menu commands. It returns NO if the
  1844.  *    DrawingView knows that action is not valid now,
  1845.  *    otherwise it returns YES.
  1846.  *
  1847.  *    Using pastecount and pastevalid prevents having to look into
  1848.  *    the Pasteboard every time paste: is validated.
  1849.  */
  1850. - (BOOL)validateCommand:menuCell
  1851. {
  1852.     SEL            action = [menuCell action];
  1853.  
  1854.     id            pasteboardId;
  1855.  
  1856.     int            count;
  1857.  
  1858.     if (    action == @selector(cut:) ||
  1859.         action == @selector(delete:) ||
  1860.         action == @selector(copy:) ||
  1861.         action == @selector(bringToFront:) ||
  1862.         action == @selector(sendToBack:) ||
  1863.         action == @selector(originalSize:) ||
  1864.         action == @selector(originalRatio:))
  1865.     {
  1866.         return([selectedlistId count] > 0);
  1867.     }
  1868.     else if (action == @selector(selectAll:))
  1869.     {
  1870.         return([graphiclistId count] > 0);
  1871.     }
  1872.     else if (action == @selector(paste:))
  1873.      {
  1874.         pasteboardId = [Pasteboard new];
  1875.         count = [pasteboardId changeCount];
  1876.         if (count != pastecount)
  1877.         {
  1878.             pastecount = count;
  1879.             pastevalid = (drawPasteType([pasteboardId  types]) != NULL);
  1880.         }
  1881.         return pastevalid;
  1882.     }
  1883.  
  1884.     return YES;
  1885. }
  1886.  
  1887. - (BOOL) knowsPagesFirst:(int *) firstPageNum  last:(int *) lastPageNum
  1888. {
  1889.     *firstPageNum = 1;
  1890.     *lastPageNum = 1;
  1891.  
  1892.     return YES;
  1893. }
  1894.  
  1895. - (BOOL) getRect:(NXRect *) theRect  forPage:(int) page
  1896. {
  1897.     if (page == 1)
  1898.     {
  1899.         *theRect = bounds;
  1900.  
  1901.         return YES;
  1902.     }
  1903.  
  1904.     return NO;
  1905. }
  1906.  
  1907. /*
  1908. *    Used when printing. Returns the global resources used in the document.
  1909. */
  1910. - addResources:(Resource *) resourceDoc
  1911. {
  1912.     NXAtom        string;
  1913.  
  1914.     if (NXDrawingStatus == NX_COPYING)
  1915.     {
  1916.         string = NXUniqueString(EpsfProcSet);
  1917.         if (!resourceDoc[RES_PROCSETS].states[RES_PRESENT])
  1918.             resourceDoc[RES_PROCSETS].states[RES_PRESENT] = [List  new];
  1919.  
  1920.         [resourceDoc[RES_PROCSETS].states[RES_PRESENT]
  1921.                         addObjectIfAbsent:(id) string]; 
  1922.  
  1923.         if (!resourceDoc[RES_PROCSETS].states[RES_SUPPLIED])
  1924.             resourceDoc[RES_PROCSETS].states[RES_SUPPLIED] = [List  new];
  1925.  
  1926.         [resourceDoc[RES_PROCSETS].states[RES_SUPPLIED]
  1927.                         addObjectIfAbsent:(id) string]; 
  1928.     }    
  1929.  
  1930.     return self;
  1931. }    
  1932.  
  1933. /*
  1934. *    Print the %%DocumentResource comments. A list of the resources is
  1935. *    accumulated from the imported files (only one in this case).
  1936. *    The list is then written to the current context.
  1937. */
  1938. - beginResourceComments:(const NXRect *) bbox
  1939. {
  1940.     int                i, j;
  1941.  
  1942.     Resource        resourceDoc[RES_NUMTYPES];
  1943.  
  1944.     bzero(&resourceDoc, sizeof(resourceDoc));
  1945.     [self  addResources:resourceDoc];
  1946.     [graphiclistId  addResources:resourceDoc  for:(NXRect *) bbox];
  1947.  
  1948.     for (i = 0; i < RES_NUMTYPES; i++)
  1949.     {
  1950.         for (j = 0; j < RES_NUMSTATES; j++)
  1951.         {
  1952.             if (resourceDoc[i].states[j])
  1953.             {
  1954.                 WriteEpsfResource(resourceDoc[i].states[j], i, j);
  1955.                 [resourceDoc[i].states[j]  free];
  1956.             }
  1957.         }
  1958.     }
  1959.  
  1960.     return self;
  1961. }
  1962.  
  1963. /*
  1964. *    Write out the necessary information. Overridden to include
  1965. *    the fonts from the EPSF files.
  1966. */
  1967. - beginPrologueBBox:(const NXRect *)boundingBox 
  1968.     creationDate:(const char *)dateCreated 
  1969.     createdBy:(const char *)anApplication 
  1970.     fonts:(const char *)fontNames 
  1971.     forWhom:(const char *)user 
  1972.     pages:(int)numPages 
  1973.     title:(const char *)aTitle
  1974. {
  1975.     time_t        clock;
  1976.  
  1977.     DPSContext    ctxt;
  1978.  
  1979.     ctxt = DPSGetCurrentContext();
  1980.     if (!boundingBox)
  1981.         boundingBox = [[NXApp  printInfo]  paperRect];
  1982.  
  1983.     if (!dateCreated)
  1984.     {
  1985.         clock = time(0);
  1986.         dateCreated = ctime(&clock);
  1987.     }
  1988.  
  1989.     if (!anApplication)
  1990.         anApplication = [NXApp  appName];
  1991.  
  1992.     if (!user)
  1993.         user = (char *) getlogin();
  1994.  
  1995.     if (numPages <= 0)
  1996.         numPages = 1;
  1997.  
  1998.     if (!aTitle)
  1999.         aTitle = [[window  delegate]  filename];
  2000.  
  2001.     DPSPrintf(ctxt, "%%!PS-Adobe-2.0 EPSF-1.2\n");
  2002.     DPSPrintf(ctxt, "%%%%Creator: %s\n", anApplication);
  2003.     DPSPrintf(ctxt, "%%%%For: %s\n", user);
  2004.     DPSPrintf(ctxt, "%%%%Title: %s\n", aTitle);
  2005.     DPSPrintf(ctxt, "%%%%CreationDate: %s", dateCreated);
  2006.     DPSPrintf(ctxt, "%%%%BoundingBox: %d %d %d %d\n", (int) floor(boundingBox->origin.x),
  2007.         (int) floor(boundingBox->origin.y),
  2008.         (int) ceil(boundingBox->origin.x + boundingBox->size.width),
  2009.         (int) ceil(boundingBox->origin.y + boundingBox->size.height));
  2010.     if (NXDrawingStatus == NX_COPYING)
  2011.         DPSPrintf(ctxt, "%%AI3_TemplateBox: %d %d %d %d\n", 306, 396, 306, 396);
  2012.     if (NXDrawingStatus != NX_COPYING)
  2013.         DPSPrintf(ctxt, "%%%%Pages: %d\n", numPages);
  2014.  
  2015.     [self  beginResourceComments:boundingBox];
  2016.  
  2017.     if ([[NXApp  printInfo]  orientation] ==  NX_LANDSCAPE)
  2018.         DPSPrintf(ctxt, "%%%%Orientation: Landscape\n");
  2019.     else
  2020.         DPSPrintf(ctxt, "%%%%Orientation: Portrait\n");
  2021.  
  2022.     return self;
  2023. }
  2024.  
  2025. /*
  2026. *    Includes the abbreviated Illustrator proc set so that
  2027. *    the imported files produced through Save To will print on their
  2028. *    own. Also includes the preview data as a comment when
  2029. *    specified.
  2030. */
  2031. - endHeaderComments
  2032. {
  2033.     DPSContext    ctxt;
  2034.  
  2035.     if (NXDrawingStatus == NX_COPYING)
  2036.     {
  2037.         ctxt = DPSGetCurrentContext();
  2038.         DPSPrintf(ctxt, "%%%%EndComments\n\n");
  2039.  
  2040.         DPSPrintf(ctxt, "%%%%BeginProcSet: EPSF_Illustrator_abbrev 0 0\n");
  2041.         WriteEpsfProcSetDef ();
  2042.         DPSPrintf(ctxt, "%%%%EndProcSet\n\n");
  2043.  
  2044.         if (imageId && [[SaveAsPanel  new]  format] == SAVE_EPSPREVIEW)
  2045.             WriteEpsfPreview(imageId);
  2046.     }
  2047.     else
  2048.         [super  endHeaderComments];
  2049.  
  2050.     return self;
  2051. }
  2052.  
  2053. /*
  2054. *    If saving in illustrator, override the prologue comment.
  2055. */
  2056. - endPrologue
  2057. {
  2058.     DPSContext    ctxt;
  2059.  
  2060.     if (NXDrawingStatus != NX_COPYING)
  2061.     {
  2062.         ctxt = DPSGetCurrentContext();
  2063.         DPSPrintf(ctxt, "%%%%EndProlog\n\n");
  2064.     }
  2065.     else
  2066.         [super  endPrologue];
  2067.  
  2068.     return self;
  2069. }
  2070.  
  2071. /*  Initialize the Illustrator abbreviated proc set. */
  2072. - beginSetup
  2073. {
  2074.     DPSContext    ctxt;
  2075.  
  2076.     if (NXDrawingStatus == NX_COPYING)
  2077.     {
  2078.         ctxt = DPSGetCurrentContext();
  2079.         DPSPrintf(ctxt, "%%%%BeginSetup\n");
  2080.         WriteEpsfProcSetInit();
  2081.     }
  2082.     else
  2083.         [super  beginSetup];
  2084.         
  2085.     return self;
  2086. }
  2087.  
  2088. - endSetup
  2089. {
  2090.     DPSContext    ctxt;
  2091.  
  2092.     if (NXDrawingStatus == NX_COPYING)
  2093.     {
  2094.         ctxt = DPSGetCurrentContext();
  2095.         DPSPrintf(ctxt, "%%%%EndSetup\n");
  2096.     }
  2097.     else
  2098.         [super  endSetup];
  2099.         
  2100.     return self;
  2101. }
  2102.  
  2103. /*  Terminate the Illustrator abbreviated proc set. */
  2104. - beginTrailer
  2105. {
  2106.     DPSContext    ctxt;
  2107.  
  2108.     if (NXDrawingStatus == NX_COPYING)
  2109.     {
  2110.         ctxt = DPSGetCurrentContext();
  2111.         DPSPrintf(ctxt, "%%%%Trailer\n");
  2112.         WriteEpsfProcSetTerm();
  2113.     }
  2114.     else
  2115.         [super  beginTrailer];
  2116.  
  2117.     return self;
  2118. }
  2119.  
  2120. - endTrailer
  2121. {
  2122.     if (NXDrawingStatus != NX_COPYING)
  2123.         [super  endTrailer];
  2124.  
  2125.     return self;
  2126. }
  2127.  
  2128. @end
  2129.